/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package session;

import event.JobAlarmEvent;
import event.JobConfirmEvent;
import event.JobSendableEvent;
import event.JobStatusEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import net.sf.appia.core.AppiaEventException;
import net.sf.appia.core.Channel;
import net.sf.appia.core.Direction;
import net.sf.appia.core.Event;
import net.sf.appia.core.Layer;
import net.sf.appia.core.Session;
import net.sf.appia.core.events.channel.ChannelInit;
import net.sf.appia.protocols.common.RegisterSocketEvent;
import util.Constants;
import util.Debug;
import util.Process;

/**
 *
 * @author Welington
 */
public class ApplicationSession extends Session {

    private ApplicationDataReader reader = null;
    private Class current = ApplicationSession.class;
    private Debug debug = Debug.getInstance();
    private ArrayList<Process> processes;
    private Process sourceProcess;

    public ApplicationSession(Layer layer) {
        super(layer);
    }

    @Override
    public void handle(Event event) {
        if (event instanceof ChannelInit) {
            handleChannelInit((ChannelInit) event);
        } else if (event instanceof RegisterSocketEvent) {
            handleRegisterSocket((RegisterSocketEvent) event);
        } else if (event instanceof JobConfirmEvent) {
            handleJobConfirm((JobConfirmEvent) event);
        } else if (event instanceof JobAlarmEvent) {
            handleJobAlarm((JobAlarmEvent) event);
        } else if (event instanceof JobStatusEvent) {
            handleJobStatus((JobStatusEvent) event);
        } else if (event instanceof JobSendableEvent) {
            handleDeliverySendableEvent((JobSendableEvent) event);
        }
    }

    private void handleChannelInit(ChannelInit event) {
        try {
            event.go();

            debug.print(Debug.INFO, current, "Registering socket");

            debug.print(Debug.LOG, current, "Revering process from file");
            processes = Constants.readFromFile();
            sourceProcess = Constants.SOURCE_PROCESS;
            debug.print(Debug.LOG, current, "Current process correctly identified {" + sourceProcess.getId() + " - " + sourceProcess.getLabel() + "}");

            RegisterSocketEvent reg = new RegisterSocketEvent(event.getChannel(), Direction.DOWN, this, sourceProcess.getPort());

            try {
                reg.localHost = InetAddress.getLocalHost();
            } catch (UnknownHostException ex) {
                debug.print(Debug.LOG, current, ex.getLocalizedMessage());
            }
            reg.init();
            reg.go();
        } catch (AppiaEventException ex) {
            debug.print(Debug.INFO, current, ex.getLocalizedMessage());
        }
    }

    private void handleRegisterSocket(RegisterSocketEvent event) {
        if (!event.error) {
            debug.print(Debug.INFO, current, "Succeded");

            if (reader == null) {
                reader = new ApplicationDataReader(event.getChannel());
            }

        } else {
            debug.print(Debug.ERROR, current, "Failed - " + event.getErrorDescription());
        }
    }

    private void handleJobConfirm(JobConfirmEvent event) {
        debug.print(Debug.INFO, current, "Received confirmation of request [" + event.getId() + "]");
        debug.print(Debug.LOG, current, "Content : " + event.getMessage());
        try {
            event.go();
        } catch (AppiaEventException ex) {
            debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
        }
    }

    private void handleDeliverySendableEvent(JobSendableEvent event) {
        if (event.getDir() == Direction.UP) {
            debug.print(Debug.INFO, current, "Message received from Transformation Layer {" + Constants.extractContent(event.getMessage()) + "}");
        }

        try {
            event.go();
        } catch (AppiaEventException ex) {
            debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
        }

    }

    private void handleJobAlarm(JobAlarmEvent alarm) {
        debug.print(Debug.INFO, current, "Received ALARM");

        try {
            alarm.go();
        } catch (AppiaEventException ex) {
            debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
        }
    }

    private void handleJobStatus(JobStatusEvent status) {
        debug.print(Debug.INFO, current, "Received status [" + status.getStatus().getLabel()
                + "] for request [" + status.getId() + "]");

        try {
            status.go();
        } catch (AppiaEventException ex) {
            debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
        }
    }

    private class ApplicationDataReader extends Thread {

        public boolean ready = false;
        public Channel channel;
        private BufferedReader stdin;
        private int rid = 0;

        public ApplicationDataReader(Channel channel) {
            stdin = new BufferedReader(new InputStreamReader(System.in));
            ready = true;
            if (this.channel == null) {
                this.channel = channel;
            }
            this.start();
        }

        @Override
        public void run() {
            boolean running = true;
            Process destinationProcess = null;
            
            debug.print(Debug.INFO, current, "What process do you want to communicate?");

            for (Process p : processes) {
                if (!p.equals(sourceProcess)) {
                    debug.print(Debug.INFO, current, "{" + p.getId() + "} " + p.getLabel());
                }
            }

            do {
                String data;
                int pid = 0;
                try {
                    data = stdin.readLine();

                    try {
                        pid = Integer.parseInt(data);
                    } catch (NumberFormatException ex) {
                        debug.print(Debug.WARN, current, "Invalid PID. Please, type a valid process ID");
                        pid = -1;
                    }

                    if (pid >= 0 && pid < processes.size()) {
                        destinationProcess = processes.get(pid);
                    } else {
                        destinationProcess = null;
                    }

                } catch (IOException ex) {
                    debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
                }
            } while (destinationProcess == null);


            while (running) {
                ++rid;
                try {
                    debug.print(Debug.INFO, current, "HANDLING REQUEST [" + rid + "] - Type the message");
                    String data = stdin.readLine();

                    JobSendableEvent event = new JobSendableEvent();
                    event.setId(rid);
                    event.dest = destinationProcess.getAddress();
                    event.setMessage(Constants.buildMessage(data));
                    event.asyncGo(channel, Direction.DOWN);

                } catch (AppiaEventException ex) {
                    debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
                } catch (IOException e) {
                    debug.print(Debug.ERROR, current, e.getLocalizedMessage());
                }

                try {
                    Thread.sleep(1500);
                } catch (Exception ex) {
                    debug.print(Debug.ERROR, current, ex.getLocalizedMessage());
                }

                synchronized (this) {
                    if (!ready) {
                        running = false;
                    }
                }
            }
        }
    }
}